#include "inifile.hpp"
#include <limits>
#include <fstream>

namespace KEG
{

IniFile::IniFile() : failbit(false)
{
    ;
}

IniFile::IniFile( const std::string& filename_ ) : failbit(false)
{
    Read( filename_ );
}

void IniFile::Read( const std::string& filename_ )
{
	std::string currentLine;
	std::string currentSect = "default";
	filename = filename_;
	std::ifstream ifs( filename.c_str() );

	if( !ifs )
	{
//		failbit = true;
		return;
	}

	while( ifs.good() && !ifs.eof() )
	{
		if( ifs.peek() == ';' || ifs.peek() == '#' )
		{
			ifs.ignore( std::numeric_limits< std::streamsize >::max(), '\n' );
			continue;
		}

		if( ifs.peek() == '[' )
		{
			ifs.ignore(1);
			std::getline( ifs, currentLine );
			//ifs.get();
			assert( currentLine.find( ']' ) == currentLine.size() - 1 );
			currentLine.erase( currentLine.size() -1 );
			currentSect = currentLine;
		}
		else
		{
			std::getline( ifs, currentLine );
			//ifs.get();
			std::string::size_type index_eq = currentLine.find( '=' );
			assert( index_eq != std::string::npos );
			var[ currentSect ][ currentLine.substr( 0, index_eq ) ] = currentLine.substr( index_eq + 1 );
		}

	}

	ifs.close();
}

void IniFile::Write( const std::string& filename_ )
{
    if( !filename_.empty() )
    {
        filename = filename_;
    }
	
	std::ofstream ofs( filename.c_str() );
	if( !ofs )
	{
		failbit = true;
		return;
	}

	ofs << "; INI File Automatically Generated by IniFile.cpp\n";

	for( std::map< std::string, std::map< std::string, std::string >>::const_iterator sect_itr = var.begin();
		sect_itr != var.end();
		++sect_itr )
	{
		ofs << '[' << sect_itr->first << "]\n";

		for( std::map< std::string, std::string >::const_iterator value_itr = sect_itr->second.begin();
			value_itr != sect_itr->second.end();
			++value_itr )
		{
			ofs << value_itr->first << '=' << value_itr->second << std::endl;
		}
	}

	ofs.close();
}

bool IniFile::Good()
{
	return !failbit;
}

void IniFile::Clear()
{
	failbit = false;
}

int IniFile::GetInt( const std::string& sect, const std::string& name, int def )
{
	
	if( var.count(sect) != 1 || var[sect].count( name ) != 1 )
    {
		std::ostringstream ostr;
		ostr << def;
		assert( ostr.good() );
		var[ sect ][ name ] = ostr.str();
		return def;
    }

    std::istringstream istr( var[ sect ][ name ] );
    int returnVal;
    istr >> returnVal;

    if( !istr.fail() )
    {   return returnVal;
    }
    else
    {   return def;
    }
}

double IniFile::GetDouble( const std::string& sect, const std::string& name, double def )
{
	if( var.count(sect) != 1 || var[sect].count( name ) != 1 )
    {
		std::ostringstream ostr;
		ostr << def;
		assert( ostr.good() );
		var[ sect ][ name ] = ostr.str();
		return def;
    }

	std::istringstream istr( var[ sect ][ name ] );
    double returnVal;
    istr >> returnVal;

    if( !istr.fail() )
    {   return returnVal;
    }
    else
    {   return def;
    }
}

std::string IniFile::GetString( const std::string& sect, const std::string& name, const std::string& def )
{
	if( var.count(sect) != 1 || var[sect].count( name ) != 1 )
    {
		var[ sect ][ name ] = def;
		return def;
    }

	return var[ sect ][ name ];
}

void IniFile::SetInt( const std::string& sect, const std::string& name, int value )
{
	std::ostringstream ostr;
	ostr << value;
	assert( ostr.good() );
	var[ sect ][ name ] = ostr.str();
}

void IniFile::SetDouble( const std::string& sect, const std::string& name, double value )
{
	std::ostringstream ostr;
	ostr << value;
	assert( ostr.good() );
	var[ sect ][ name ] = ostr.str();
}

void IniFile::SetString( const std::string& sect, const std::string& name, const std::string& value )
{
	var[ sect ][ name ] = value;
}


}
